/**
 * SSE控制器
 * 处理Server-Sent Events通信
 */
const { generateId } = require('../utils/id-generator');
const { log } = require('../middleware/logger');

class SSEController {
  constructor() {
    this.connections = new Map(); // 存储活跃连接
  }

  /**
   * 建立SSE连接
   * @param {Object} ctx - Koa上下文
   */
  async connect(ctx) {
    const connectionId = generateId();

    // 设置SSE响应头
    ctx.set({
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      'Connection': 'keep-alive',
      'Access-Control-Allow-Origin': '*',
      'Access-Control-Allow-Headers': 'Cache-Control'
    });
    // 显式设置200状态码，避免默认404
    ctx.status = 200;

    // 创建连接对象
    const connection = {
      id: connectionId,
      ctx,
      createdAt: new Date(),
      lastActivity: new Date(),
      isActive: true
    };

    // 存储连接
    this.connections.set(connectionId, connection);

    log.info('SSE连接建立', { connectionId, totalConnections: this.connections.size });

    // 发送连接确认消息
    this.sendEvent(connectionId, 'connected', {
      connectionId,
      timestamp: new Date().toISOString(),
      message: '连接已建立'
    });

    // 设置心跳
    const heartbeat = setInterval(() => {
      if (connection.isActive) {
        this.sendEvent(connectionId, 'heartbeat', {
          timestamp: new Date().toISOString()
        });
      } else {
        clearInterval(heartbeat);
      }
    }, 30000); // 每30秒发送心跳

    // 监听连接关闭
    ctx.req.on('close', () => {
      this.closeConnection(connectionId);
      clearInterval(heartbeat);
    });

    ctx.req.on('error', (error) => {
      log.error('SSE连接错误', { connectionId, error: error.message });
      this.closeConnection(connectionId);
      clearInterval(heartbeat);
    });

    // 保持连接开启
    ctx.respond = false;
  }

  /**
   * 发送事件到指定连接
   * @param {string} connectionId - 连接ID
   * @param {string} event - 事件类型
   * @param {Object} data - 事件数据
   */
  sendEvent(connectionId, event, data) {
    const connection = this.connections.get(connectionId);

    if (!connection || !connection.isActive) {
      log.warn('尝试向无效连接发送事件', { connectionId, event });
      return false;
    }

    try {
      const eventData = JSON.stringify(data);
      const message = `event: ${event}\ndata: ${eventData}\n\n`;

      connection.ctx.res.write(message);
      connection.lastActivity = new Date();

      return true;
    } catch (error) {
      log.error('发送SSE事件失败', { connectionId, event, error: error.message });
      this.closeConnection(connectionId);
      return false;
    }
  }

  /**
   * 发送消息事件
   * @param {string} connectionId - 连接ID
   * @param {Object} messageData - 消息数据
   */
  sendMessage(connectionId, messageData) {
    return this.sendEvent(connectionId, 'message', {
      id: generateId(),
      timestamp: new Date().toISOString(),
      ...messageData
    });
  }

  /**
   * 发送状态事件
   * @param {string} connectionId - 连接ID
   * @param {string} status - 状态
   * @param {string} message - 状态消息
   */
  sendStatus(connectionId, status, message = '') {
    return this.sendEvent(connectionId, 'status', {
      status,
      message,
      timestamp: new Date().toISOString()
    });
  }

  /**
   * 发送错误事件
   * @param {string} connectionId - 连接ID
   * @param {string} error - 错误信息
   * @param {string} code - 错误代码
   */
  sendError(connectionId, error, code = 'UNKNOWN_ERROR') {
    return this.sendEvent(connectionId, 'error', {
      error,
      code,
      timestamp: new Date().toISOString()
    });
  }

  /**
   * 发送工具调用开始事件
   * @param {string} connectionId - 连接ID
   * @param {Object} toolCall - 工具调用信息
   */
  sendToolCallStart(connectionId, toolCall) {
    return this.sendEvent(connectionId, 'tool_call_start', {
      toolCall,
      timestamp: new Date().toISOString()
    });
  }

  /**
   * 发送工具调用结果事件
   * @param {string} connectionId - 连接ID
   * @param {Object} toolCall - 工具调用信息
   * @param {string} result - 工具执行结果
   */
  sendToolCallResult(connectionId, toolCall, result) {
    return this.sendEvent(connectionId, 'tool_call_result', {
      toolCall,
      result,
      timestamp: new Date().toISOString()
    });
  }

  /**
   * 发送流式文本块
   * @param {string} connectionId - 连接ID
   * @param {string} content - 文本内容
   * @param {boolean} isComplete - 是否完成
   */
  sendTextChunk(connectionId, content, isComplete = false) {
    return this.sendEvent(connectionId, 'text_chunk', {
      content,
      isComplete,
      timestamp: new Date().toISOString()
    });
  }

  /**
   * 关闭连接
   * @param {string} connectionId - 连接ID
   */
  closeConnection(connectionId) {
    const connection = this.connections.get(connectionId);

    if (connection) {
      connection.isActive = false;

      try {
        connection.ctx.res.end();
      } catch (error) {
        log.warn('关闭SSE连接时出错', { connectionId, error: error.message });
      }

      this.connections.delete(connectionId);
      log.info('SSE连接关闭', { connectionId, totalConnections: this.connections.size });
    }
  }

  /**
   * 获取连接信息
   * @param {string} connectionId - 连接ID
   * @returns {Object|null} 连接信息
   */
  getConnection(connectionId) {
    return this.connections.get(connectionId) || null;
  }

  /**
   * 获取所有活跃连接
   * @returns {Array} 连接列表
   */
  getActiveConnections() {
    return Array.from(this.connections.values()).filter(conn => conn.isActive);
  }

  /**
   * 清理过期连接
   */
  cleanupExpiredConnections() {
    const now = new Date();
    const expireTime = 60 * 60 * 1000; // 1小时过期

    for (const [connectionId, connection] of this.connections.entries()) {
      if (now - connection.lastActivity > expireTime) {
        this.closeConnection(connectionId);
        log.debug('清理过期SSE连接', { connectionId });
      }
    }
  }

  /**
   * 广播消息到所有连接
   * @param {string} event - 事件类型
   * @param {Object} data - 事件数据
   */
  broadcast(event, data) {
    const activeConnections = this.getActiveConnections();

    activeConnections.forEach(connection => {
      this.sendEvent(connection.id, event, data);
    });

    log.debug('广播SSE事件', { event, connectionCount: activeConnections.length });
  }
}

// 创建单例实例
const sseController = new SSEController();

// 定期清理过期连接
setInterval(() => {
  sseController.cleanupExpiredConnections();
}, 10 * 60 * 1000); // 每10分钟清理一次

module.exports = sseController;